
#include "BPlusTree.h"
#include "IndexRecord.h"
#include "Page.h"
#include "Attribute.h"
#include <algorithm>
#include <iostream>
#include <vector>

// This is the extension for the b+tree info file
const char* EXT = ".info";

const char* INFO_FIELDS[] = {"keyType","numPages","height","rootLocation"};

int * BPlusTree:: SearchKeyRange (int &numPagesReturned, Attribute &lowVal, 
		Attribute &highVal)
{
	numPagesReturned = 0;
	std::vector<int> v( numPagesReturned );
	std::deque<Page>::iterator it;
	
	for (it=m_lPageList.begin(); it != m_lPageList.end(); ++it)
	{
		IndexRecord irec;
		while( it->GetFirst(irec) )
		{
			if( (irec).Compare( lowVal, highVal, m_oKeyType) == 0)
			{
				v.push_back( (irec).GetPointer() );
				++numPagesReturned;
			}
		}
	}
	
	std::sort(v.begin(), v.end());
	std::unique(v.begin(), v.end());
	v.resize(numPagesReturned);
	
	int* return_list = new int[numPagesReturned];

	for(int i=0; i < numPagesReturned; ++i)
		return_list[i] = v[i];
		
	return (int*)return_list;
}


void BPlusTree:: SetIndexRecord ( IndexRecord & _ir, char *keyVal,
		int _pointer )
{
	//assert(m_oKeyType);
	
	switch( m_oKeyType )
	{
		case Int:
		{
			_ir.Load(keyVal, sizeof(int), _pointer);
		}
		case Double:
		{
			_ir.Load(keyVal, sizeof(double), _pointer);
		}
		case String:
		{
			_ir.Load(keyVal, strlen(keyVal), _pointer);
		}
		default:
			std::cerr<<"default:"<<__LINE__<< std::endl;
			break;
	}
}


void BPlusTree:: FirstInsert (char *keyVal, int pageID)
{
	//assert(m_oKeyType);
	
	IndexRecord inLeaf;
	IndexRecord inRoot;
	
	SetIndexRecord (inRoot, keyVal, 1);
	SetIndexRecord (inLeaf, keyVal, pageID);
		
	//This is the basic case
	// TODO change append to return the number of bytes written
	
	m_lPageList[m_iRootLocation].Append( inRoot );
	m_lPageList[m_iRootLocation+1].Append( inLeaf );
		
}

void BPlusTree:: Insert(char *keyVal, int pageID)
{
	if(record_count > 0)
	{
		//IndexRecursive(keyVal, pageID, m_iHeight, int currentPageNo)
	}
	else
	{
		++record_count;
		FirstInsert(keyVal, pageID);
	}
	
	
	//The insert algorithm for B+ Trees ----------------------------
	// Leaf Page = LP
	// Index Page = IP
	// 0 equals empty not full, 1 = full page
	
	// case #1
	// if (LP = 0, IP = 0)
	// Place the record in sorted position in the appropriate leaf page
	
	// case #2
	//else if (LP = 1, IP = 0)
		//1. Split the leaf page
		//2. Place Middle Key in the index page in sorted order.
		//3. Left leaf page contains records with keys below the middle key.
		//Right leaf page contains records with keys equal to or greater than
		//the middle key.
	
	// case #3
	//else if(LP = 1, IP = 0)
		//1. Split the leaf page.
		//2. Records with keys < middle key go to the left leaf page.
		//3. Records with keys >= middle key go to the right leaf page.
		//4. Split the index page.
		//5. Keys < middle key go to the left index page.
		//6. Keys > middle key go to the right index page.
		//7. The middle key goes to the next (higher level) index.
		//IF the next level index page is full, continue splitting the index
		//pages.
        
		
}

/*
void BPlusTree:: Insert(IndexRecord ir_rec)
{
  
}
*/

void BPlusTree:: IndexRecursive(char *keyVal, int pageID, int currHeight, int currentPageNo)
{
	if(currHeight == 0) // We are at a leaf
	{
		IndexRecord ir;
		SetIndexRecord( ir, keyVal, pageID );
		m_lPageList[currentPageNo].Append( ir );
		
	}
	else	// We are not at a leaf
	{
		IndexRecord ir;
		Attribute at;
		ir.GetKey( at, m_oKeyType );
		
		Page p = m_lPageList[currentPageNo];
		
		bool path_taken = false;
		while(p.GetFirst( ir ) && !path_taken)
		{
			
			int i = ir.Compare (at, at, m_oKeyType);
			if( i <= 0 )
			{
				path_taken = true;
				IndexRecursive(keyVal, pageID, currHeight-1,  ir.GetPointer());
			}
		}
				
	}
}


BPlusTree:: BPlusTree ()
{	
	m_iHeight = 0;
	m_iNumPages = 0;
	m_iRootLocation = -1;
	record_count = 0;
}

BPlusTree:: BPlusTree (Type _type)
{
	/*
	m_iHeight = 2;
	m_iNumPages = 2;
	m_iRootLocation = 0;
	
	//Create root record
	IndexRecord root;
	
	switch (_type)
	{
		case Int:
		{
			char buf[ sizeof(int)];
			int neginf = -32767;
			sprintf( buf, "%d", neginf );
			root.Load( buf, sizeof(int), 1);
			break;
		}
		case Double:
		{
			char buf[ sizeof(double)];
			double neginf = -1e309;
			sprintf( buf, "%f", neginf );
			root.Load( buf, sizeof(double), 1);
			break;
		}
		case String:
		{
			char* buf = NULL;
			root.Load( buf, sizeof(char*), 1);
			break;
		}
		default:
			std::cout<<"default:"<<__LINE__<<std::endl;
			break;
	}
	
	Page page0;
	page0.Append(root);
	
	// Put root inside the page List
	m_lPageList.push_back(page0);
	
	Page page1;
	// put an empty page inside the page list
	m_lPageList.push_back(page1);
	*/

}


BPlusTree:: ~BPlusTree ()
{
	//close open bplus file
	m_oWhereILive.Close();


	// Prepare info file name
	int if_sz = strlen(m_pFileName) + strlen(EXT);
	char infofile[if_sz];
	strcpy(infofile, m_pFileName);
	strcat(infofile, EXT);

	//create and write out the info file
	FILE * f_info;
	f_info = fopen (infofile,"w"); 
	
	if( f_info == NULL){
		std::cout<<"default:"<<__LINE__<< " File could noe be opened" << std::endl;
		exit(1);
	}
	
	//print to the .info file
	char* typ_int = "Int";
	char* typ_double = "Double";
	char* typ_string = "String";
	char typeName[15];
	switch(m_oKeyType)
	{
		case Int:
			strcpy(typeName, typ_int);
			break;
		case Double:
			strcpy(typeName, typ_double);
			break;
		case String:
			strcpy(typeName, typ_string);
			break;
		default:
			std::cout<<"default:"<<__LINE__<< "Bad type chosen" << std::endl;
			break;
	}
	
	fprintf( f_info, "keyType: %s\nnumPages: %d\n,height: %d\n, rootLocation: %d\nrecord_count %d\n", typeName, m_iNumPages, m_iHeight, m_iRootLocation, record_count);
	
	fclose( f_info );
	
	delete m_pFileName;	
	
	//More destruction
	
	//m_lPageList.erase( m_lPageList.begin(), m_lPageList.end() );
	
	
}


int BPlusTree:: Create (char *fName, Type keyType)
{
	m_iHeight = 1;
	//m_iNumPages = 2;
	m_iNumPages = 0;
	m_iRootLocation = 0;
	
	//Create root record
	IndexRecord root;
	
	m_oKeyType = keyType;
	
	switch ( m_oKeyType )
	{
		case Int:
		{
			char buf[ sizeof(int)];
			int neginf = -32767;
			sprintf( buf, "%d", neginf );
			root.Load( buf, sizeof(int), 1);
			break;
		}
		case Double:
		{
			char buf[ sizeof(double)];
			double neginf = -1e309;
			sprintf( buf, "%f", neginf );
			root.Load( buf, sizeof(double), 1);
			break;
		}
		case String:
		{
			char* buf = "";
			root.Load( buf, sizeof(char*), 1);
			break;
		}
		default:
			std::cout<<"default:"<<__LINE__<<std::endl;
			break;
	}
	
	//Page page0;
	//page0.Append(root);
	
	// Put root inside the page List
	//m_lPageList.push_back(page0);
	
	//Page page1;
	// put an empty page inside the page list
	//m_lPageList.push_back(page1);
	
	m_pFileName = new char[strlen(fName)];
	strcpy(m_pFileName, fName);
	
	m_oWhereILive.Open(0, fName);
	
	return 1;

}


int BPlusTree::Open (char *fName)
{
	// Prepare info file name
	char infofile[strlen(fName)+strlen(EXT)];
	
	//set the member pointer file name
	strcpy(m_pFileName, fName);

	strcpy(infofile, m_pFileName);
	strcat (infofile, EXT);

	//create and write out the info file
	FILE * f_info;
	f_info = fopen (infofile,"r"); 

	if(f_info == NULL){
		std::cerr<<"default:"<<__LINE__<<std::endl;
		return 0;
	}
	

	char* f_info_text = "keyType: %s\nnumPages: %d\n,height: %d\n, rootLocation: %d\nrecord_count %d\n";
	char typ[15];
	fscanf( f_info, f_info_text, typ, m_iNumPages, m_iHeight, m_iRootLocation, record_count); 

	if(strcmp(typ,"Int"))
	{
		m_oKeyType = Int;
	}
	else if (strcmp(typ,"Double"))
	{
		m_oKeyType = Double;
	}
	else
	{
		m_oKeyType = String;
	}
	
	m_oWhereILive.Open(1,m_pFileName);
	return 1;
}

